﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Text;
using Milan.Diagnostics;

namespace Milan.Deploy
{
	public partial class Client
	{
		private bool m_allUsers = false;
		/// <summary>
		/// Get the install command.
		/// </summary>
		public string InstallCommand
		{
			get
			{
				if (m_installCommand == null)
					m_installCommand = ParseCommandFile("install.txt");
				return m_installCommand;
			}
		}
		private string m_installCommand;
		/// <summary>
		/// Installation Directory - Where the files are first copied to and perhaps reside there.
		/// </summary>
		public string InstallationDirectory
		{
			get
			{
				if (m_installationDirectory == null)
				{

				}
				return m_installationDirectory;
			}
			set
			{
				m_installationDirectory = value;
			}
		}
		private string m_installationDirectory;
		/// <summary>
		/// Installs the links relative to the links file and relative to 
		/// StartMenu\Manufacturer\Product Name
		/// </summary>
		/// <param name="linksFile"></param>
		/// <param name="allUsers">true=install for all users</param>
		private void InstallLinks(string linksFile)
		{
			string baseLinkDirectory = Environment.GetFolderPath(m_allUsers ? Environment.SpecialFolder.CommonStartMenu : Environment.SpecialFolder.StartMenu, Environment.SpecialFolderOption.Create);
			string baseProductLinkDirectory = Path.Combine(baseLinkDirectory, Settings.CompanyName, Settings.ProductName.Replace('_', ' '));
			Directory.CreateDirectory(baseProductLinkDirectory);
			foreach (string line in File.ReadAllLines(linksFile))
			{
				string[] parameters = Milan.Text.Parser.ParseCSV(line);
				var shellLink = new Milan.Install.ShellLink();
				shellLink.Description = parameters[0];
				// Target path
				if (Path.IsPathRooted(parameters[1]))
					shellLink.Target = parameters[1];
				else
					shellLink.Target = Path.Combine(InstallationDirectory, parameters[1]);
				// Command line arguments
				shellLink.Arguments = parameters[2];
				// Path to this link
				if (Path.IsPathRooted(parameters[3]))
					shellLink.ShortCutFile = parameters[3];
				else
					shellLink.ShortCutFile = Path.Combine(baseProductLinkDirectory, Path.ChangeExtension(parameters[3],"lnk"));
				// Set the icon path
				if (Path.IsPathRooted(parameters[4]))
					shellLink.IconPath = parameters[4];
				else
					shellLink.IconPath = Path.Combine(InstallationDirectory, parameters[4]);
				shellLink.IconIndex = Convert.ToInt32(parameters[5]);
				// Set the working directory
				if (Path.IsPathRooted(parameters[6]))
					shellLink.WorkingDirectory = parameters[6];
				else
					shellLink.WorkingDirectory = Path.Combine(InstallationDirectory, parameters[6]);
				shellLink.Save();
			}
		}
		private void UninstallLinks(string linksFile)
		{
			string baseLinkDirectory = Environment.GetFolderPath(m_allUsers ? Environment.SpecialFolder.CommonStartMenu : Environment.SpecialFolder.StartMenu, Environment.SpecialFolderOption.Create);
			string baseProductLinkDirectory = Path.Combine(baseLinkDirectory, Settings.CompanyName, Settings.ProductName.Replace('_', ' '));
			foreach (string line in File.ReadAllLines(linksFile))
			{
				string[] parameters = Milan.Text.Parser.ParseCSV(line);
				// Path to this link
				if (Path.IsPathRooted(parameters[3]))
					File.Delete(parameters[3]);
				else
					File.Delete(Path.Combine(baseProductLinkDirectory, Path.ChangeExtension(parameters[3], "lnk")));
			}
		}
		private void UninstallLinks(Version versionLink)
		{
			string linksFile = Path.Combine(Settings.ProductFolder, FormatVersionDirectory(versionLink), "links.txt");
			UninstallLinks(linksFile);
		}
		/// <summary>
		/// Reads the command file
		/// </summary>
		/// <param name="fileName">name of the file relative to the installation folder</param>
		/// <returns></returns>
		public string ParseCommandFile(string fileName)
		{
			string contents = null;
			string directory = Settings.VersionFolder;
			string installtxt = Path.Combine(directory, fileName);
			if (File.Exists(installtxt))
				contents = File.ReadAllText(installtxt);
			if(contents != null)
				return Path.Combine(directory,contents);
			return null;
		}
		/// <summary>
		/// This will check-in and report an installation.
		/// </summary>
		/// <param name="productName"></param>
		/// <param name="machineId"></param>
		public void ReportInstallation()
		{
			var postVars = SetPostVars(OpCodes.install, "username&password");
			foreach (Uri uri in UploadScriptUris)
				Milan.Net.Upload.PostVarsOnly(uri, postVars);
		}
		/// <summary>
		/// This runs a command synchronously within the staging directory.
		/// </summary>
		/// <param name="commandLine">Command line to execute, including executable name...</param>
		/// <returns>error code</returns>
		private int RunStagingCommand(string commandLine)
		{
			string errMessage;
			int i = commandLine.IndexOf(' ');
			var processStartInfo = new ProcessStartInfo(commandLine.Substring(0, i), commandLine.Substring(i));
			processStartInfo.UseShellExecute = false;
			processStartInfo.WorkingDirectory = Settings.VersionFolder;
			processStartInfo.CreateNoWindow = false;
			processStartInfo.ErrorDialog = false;
			processStartInfo.RedirectStandardError = true;
			processStartInfo.RedirectStandardInput = true;
			processStartInfo.RedirectStandardOutput = true;
			Process process = new Process();
			process.StartInfo = processStartInfo;
			StreamWriter inputWriter = process.StandardInput;
			StreamReader outputReader = process.StandardOutput;
			StreamReader errorReader = process.StandardError;
			errMessage = "OUTPUT:" + outputReader.ReadToEnd() + Environment.NewLine;
			errMessage += "ERROR:" + errorReader.ReadToEnd();
			process.WaitForExit();
			// If this command experienced an error, then mark it as critical and return it.
			if (process.ExitCode != 0)
				log.Critical(errMessage);
			return process.ExitCode;
		}
		/// <summary>
		/// Perform a test of this package
		/// </summary>
		/// <returns>Returns true if the test succeeds.</returns>
		public bool Test()
		{
			bool testSuccess = false;
			testSuccess = RunStagingCommand(TestCommand) == 0;
			return testSuccess;
		}
		/// <summary>
		/// This is the test command for this package
		/// </summary>
		public string TestCommand
		{
			get
			{
				if (m_testCommand == null)
					m_testCommand = ParseCommandFile("test.txt");
				return m_testCommand;
			}
			set
			{
				m_testCommand = value;
			}
		}
		private string m_testCommand;
		/// <summary>
		/// This is the un-install command for this package.
		/// </summary>
		public string UninstallCommand
		{
			get
			{
				if (m_uninstallCommand == null)
					m_uninstallCommand = ParseCommandFile("uninstall.txt");
				return m_uninstallCommand;
			}
			set
			{
				m_uninstallCommand = value;
			}
		}
		private string m_uninstallCommand;
		/// <summary>
		/// Perform an upgrade install.
		/// This performs an upgrade depending upon the state.
		/// </summary>
		/// <returns>Return true if the upgrade was successful</returns>
		public bool UpgradeNow()
		{
			bool returns = false;
			// First we check if a downloaded version already exists
			Version downloadedVersion = Version.Parse(Settings.ProductMachine.GetValue("LastDownloadedVersion", "0.0"));
			Version remoteVersion;
			Uri remoteUri;
			// If we can get information about the latest version, then check against this remote version.
			if (GetLatestVersionInfo(out remoteVersion, out remoteUri))
			{
				if (downloadedVersion < remoteVersion)	// The server has a later version
				{
					// Download the latest version
					if( DownloadVersionFiles(remoteUri, remoteVersion, Settings.ProductFolder) )
						downloadedVersion = remoteVersion;
				}
			}
			// Should we upgrade this software?
			if (downloadedVersion > CurrentVersion)
			{
				Version lastInstalledVersion = Version.Parse(Settings.ProductMachine.GetValue("LastInstalledVersion", "0.0"));
				if (lastInstalledVersion!=(new Version(0,0)))
				{ 	// Uninstall the last version
					this.UninstallLinks(lastInstalledVersion);
				}
				// Install the links for the current version.
				InstallLinks(Path.Combine(InstallationDirectory, "links.txt"));
				Settings.ProductMachine.SetValue("LastInstalledVersion", lastInstalledVersion.ToString());
			}
			return returns;
		}
	}
}
